define([
    'app',
    'rtext!views/pages/templates/mybatis-mapper.dao.txt',
    'rtext!views/pages/templates/mybatis-mapper.model.txt',
    'rtext!views/pages/templates/mybatis-mapper.mapper.txt',
    'ace-language-tools'
], function(app, daoTemplate, modelTemplate, mapperTemplate) {
    app.controller('MyBatisMapperController', function($scope, $timeout, xUtil, xSqlParser) {
        var self = this;

        self.activeTab = 0;
        self.daoName = '';
        self.modelName = '';
        self.mapperName = '';
        self.templates = { dao: '', model: '', mapper: '' };
        self.error = '';
        self.hasSql = false;
        self.hasCode = false;

        var parameters = {
            table: { name: '', columns: [
                { name: '', jdbcType: '', javaName: '', javaType: '', primaryKey: false, comment: '' }
            ]},
            dao: { name: '', packageName: '', className: '' },
            model: { name: '', packageName: '', className: '', jpa: false, lombok: false },
            mapper: { name: '' }
        };

        var sqlEditor = null,
            daoViewer = null,
            modelViewer = null,
            mapperViewer = null,
            javaOptions = {
                mode: 'ace/mode/java',
                fontSize: 14,
                readOnly: true,
                useWorker: false,
                showPrintMargin: false,
                highlightGutterLine: false,
                highlightActiveLine: false,
                autoScrollEditorIntoView: true
            };

        function getPackageName(name) {
            var dotIndex = name.lastIndexOf('.');
            return (dotIndex == -1 ? '' : name.substring(0, dotIndex));
        }

        function getClassName(name) {
            var dotIndex = name.lastIndexOf('.');
            return (dotIndex == -1 ? name : name.substring(dotIndex + 1));
        }

        self.init = function() {
            self.templates.dao = daoTemplate.trim();
            self.templates.model = modelTemplate.trim();
            self.templates.mapper = mapperTemplate.trim();

            $scope.$watch('vm.daoName', function(newValue) {
                parameters.dao['name'] = newValue;
                parameters.dao['packageName'] = getPackageName(newValue);
                parameters.dao['className'] = getClassName(newValue);
            });

            $scope.$watch('vm.modelName', function(newValue) {
                parameters.model['name'] = newValue;
                parameters.model['packageName'] = getPackageName(newValue);
                parameters.model['className'] = getClassName(newValue);
            });

            $scope.$watch('vm.mapperName', function(newValue) {
                parameters.mapper['name'] = newValue;
            });

            $timeout(function() {
                // sql editor
                sqlEditor = ace.edit('sql-editor', {
                    mode: 'ace/mode/sql',
                    theme: 'ace/theme/chrome',
                    fontSize: 14,
                    showPrintMargin: false,
                    autoScrollEditorIntoView: true,
                    enableBasicAutocompletion: true,
                    enableSnippets: true,
                    enableLiveAutocompletion: true
                });
                sqlEditor.on('change', function(e) {
                    var hasSql = !/^\s*$/.test(sqlEditor.getValue());
                    if (self.hasSql != hasSql) {
                        self.hasSql = hasSql;
                        $timeout(function() { $scope.$apply(); });
                    }
                });
                $timeout(function() {
                    sqlEditor.resize();
                });

                // dao viewer
                daoViewer = ace.edit('dao-viewer', javaOptions);
                // model viewer
                modelViewer = ace.edit('model-viewer', javaOptions);
                // mapper viewer
                mapperViewer = ace.edit('mapper-viewer', {
                    mode: 'ace/mode/xml',
                    theme: 'ace/theme/chrome',
                    fontSize: 14,
                    readOnly: true,
                    useWorker: false,
                    showPrintMargin: false,
                    highlightGutterLine: false,
                    highlightActiveLine: false,
                    autoScrollEditorIntoView: true
                });

                xUtil.clipboard.copy('#copy', function() {
                    if (self.activeTab == 0) {
                        return daoViewer.getValue();
                    } else if (self.activeTab == 1) {
                        return modelViewer.getValue();
                    } else if (self.activeTab == 2) {
                        return mapperViewer.getValue();
                    }
                });
            });
        }

        self.callback = function(height, width) {
            // size of input editor
            jQuery('.x-mapper:eq(0) .tab-pane').css('height', (height - 229) + 'px');
            jQuery('.x-mapper:eq(0) .tab-pane pre').css('min-height', (height - 229) + 'px');
            // size of output editor
            jQuery('.x-mapper:eq(1) .tab-pane').css('height', (height - 57) + 'px');
            jQuery('.x-mapper:eq(1) .tab-pane pre').css('height', (height - 57) + 'px');
        }

        self.save = function() {
            if (self.activeTab == 0) {
                xUtil.file.save(parameters.dao['className'] + '.java', daoViewer.getValue());
            } else if (self.activeTab == 1) {
                xUtil.file.save(parameters.model['className'] + '.java', modelViewer.getValue());
            } else if (self.activeTab == 2) {
                xUtil.file.save(parameters.mapper['name'] + '.xml', mapperViewer.getValue());
            }
        }

        self.reset = function() {
            self.activeTab = 0;
            self.daoName = '';
            self.modelName = '';
            self.mapperName = '';
            self.error = '';
            self.hasSql = false;
            self.hasCode = false;
            // editor & viewer
            sqlEditor.setValue('', -1);
            daoViewer.setValue('', -1);
            modelViewer.setValue('', -1);
            mapperViewer.setValue('', -1);
        }

        self.mapping = function(valid, type) {
            daoViewer.setValue('', -1);
            modelViewer.setValue('', -1);
            mapperViewer.setValue('', -1);
            self.error = '';
            self.hasCode = false;

            if (valid) {
                parameters.table.name = '';
                parameters.table.columns = [];
                parameters.model.jpa = /jpa/g.test(type);
                parameters.model.lombok = /lombok/g.test(type);

                var schema = xSqlParser.parse(sqlEditor.getValue(), /\{((jdbcType|javaType):([0-9A-Za-z]+),?)+\}/g,
                        matchCustomizedType);
                if (schema) {
                    parameters.table.name = schema.table.name;
                    parameters.table.columns = schema.columns;
                }

                try {
                    daoViewer.setValue(applyTemplate('dao'), -1);
                    daoViewer.resize();
                    modelViewer.setValue(applyTemplate('model'), -1);
                    modelViewer.resize();
                    mapperViewer.setValue(applyTemplate('mapper'), -1);
                    mapperViewer.resize();
                    // enable copy & save
                    self.hasCode = true;
                } catch (e) {
                    self.error = e;
                }
            }
        }

        function matchCustomizedType(column, input) {
            if (input.match(/\{((jdbcType|javaType):([0-9A-Za-z]+),?)+\}/g)) {
                var rtype = /([A-Za-z]+):([0-9A-Za-z]+)/g,
                    match = null;
                while ((match = rtype.exec(input))) {
                    column[match[1]] = match[2];
                }
            }
        }

        function applyTemplate(templateName) {
            return _.template(self.templates[templateName])(parameters).trim();
        }
    });
});
